package ro.qi.framework.sys.media.utils;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import ro.qi.framework.sys.debug.Logger;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class ImageUtil {

	public static final int IMAGE_TYPE_UNKNOWN				= 0;
	public static final int IMAGE_TYPE_JPG					= 1;
	public static final int IMAGE_TYPE_GIF					= 2;
	public static final int IMAGE_TYPE_PNG					= 3;
	public static final int IMAGE_TYPE_BMP					= 4;
	public static final int IMAGE_TYPE_TIFF					= 5;

	public static void resizeImage(InputStream input, OutputStream output, int width, int height, String contentType) {

		BufferedImage image = decodeImage(input, contentType);

		if (width != 0 && height != 0) {
			AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance((float)((float)width / (float)image.getWidth()), (float)((float)height / (float)image.getHeight())), null);
			image = op.filter(image, null);
		} else if (width != 0 || height != 0) {
			if (height != 0) {
				int w = (int)((float)image.getWidth()*(float)((float)height/(float)image.getHeight()));
				image = scaleToSize(w, height, image);
			} else {
				int h = (int)((float)image.getHeight()*(float)((float)width/(float)image.getWidth()));
				image = scaleToSize(width, h, image);
			}
		}
		//image = image.getSubimage(50,0,250,366);
		if (image != null) {
			encodeImage(image, output, contentType);
		}
	}

	public static void resizeImageToSquare(InputStream input, OutputStream output, int width, int height, String contentType) {
//		System.err.println("resizeImageToSquare called");
		BufferedImage image = decodeImage(input, contentType);
		System.setProperty("java.awt.headless","true");
		
//		resizeImage(input,output,width,height,contentType);
		if(image.getWidth()>image.getHeight()) {
			
			//BufferedImage newimage = new BufferedImage(image.getColorModel(),raster, true, null);	
			BufferedImage ni = new BufferedImage(width,width,image.getType());
			ni.getGraphics().setColor(Color.white);
			ni.getGraphics().fillRect(0,0,width,width);
			WritableRaster wr = ni.getRaster();
			
			int h = (int)((float)image.getHeight()*(float)((float)width/(float)image.getWidth()));
			image = scaleToSize(width, h, image);
			
			WritableRaster raster = image.getRaster();
			raster = image.copyData(raster);
			
			wr.setRect(0,(width-image.getHeight())/2,raster);
			//newimage.setData(raster);
			encodeImage(ni, output, contentType);
			return;
			
		}
		else if(image.getWidth()<image.getHeight()) {
			BufferedImage ni = new BufferedImage(width,width,image.getType());
			ni.getGraphics().setColor(Color.white);
			ni.getGraphics().fillRect(0,0,width,width);
			WritableRaster wr = ni.getRaster();

			int w = (int)((float)image.getWidth()*(float)((float)height/(float)image.getHeight()));
			image = scaleToSize(w, height, image);

			WritableRaster raster = image.getRaster();
			raster = image.copyData(raster);

			wr.setRect((width-image.getWidth())/2,0,raster);
			//newimage.setData(raster);
			encodeImage(ni, output, contentType);
			return;
		}
		else{
			AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance((float)((float)width / (float)image.getWidth()), (float)((float)height / (float)image.getHeight())), null);
			image = op.filter(image, null);
			encodeImage(image, output, contentType);
		}
	}

	public static int[] resizeImage2(InputStream input, OutputStream output, int width, int height, String contentType) {
		BufferedImage image = decodeImage(input, contentType);
		int[] dims = new int[2];
		if (image != null) {
			dims[0] = image.getWidth();
			dims[1] = image.getHeight();
			if (image.getWidth() > image.getHeight()) {
				int w = (int)((float)image.getWidth()*(float)((float)height/(float)image.getHeight()));
				image = scaleToSize(w, height, image);
				image = image.getSubimage((w-width)/2,0,width,height);
			} else
			if (image.getHeight() > image.getWidth()) {
				int h = (int)((float)image.getHeight()*(float)((float)width/(float)image.getWidth()));
				image = scaleToSize(width, h, image);
				image = image.getSubimage(0,(h-height)/2,width,height);
			} else
			{
				image = scaleToSize(width, height, image);
			}
			encodeImage(image, output, contentType);
		}
		return dims;
	}

	public static BufferedImage decodeImage(InputStream input, String contentType) {
		BufferedImage bi = null;
		try {
			switch (getImageType(contentType)) {
				case IMAGE_TYPE_JPG:
				{
					JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(input);
					bi = decoder.decodeAsBufferedImage();
				}
				break;
				case IMAGE_TYPE_GIF:
				{
					GifDecoder d = new GifDecoder();
					int err = d.read(input);
					bi = d.getImage();
				}
				break;
			}
		} catch (Exception e) {
			Logger.error("Error decoding image: " + e.getMessage());
		}
		return bi;
	}

	public static BufferedImage scaleToSize(int nMaxWidth, int nMaxHeight, BufferedImage imgSrc) {
		int nHeight = imgSrc.getHeight();
		int nWidth = imgSrc.getWidth();
		double scaleX = (double) nMaxWidth / (double) nWidth;
		double scaleY = (double) nMaxHeight / (double) nHeight;
		double fScale = Math.min(scaleX, scaleY);
		return scale(fScale, imgSrc);
	}

	public static BufferedImage scale(double scale, BufferedImage srcImg) {
		if (scale == 1) {
			return srcImg;
		}
		AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), null);
		return op.filter(srcImg, null);
	}

	public static void encodeImage(BufferedImage bi, OutputStream out, String contentType) {
		if (bi != null && out != null) {
			try {
				switch (getImageType(contentType)) {
					case IMAGE_TYPE_JPG:
					{
						JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
						JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
						param.setQuality(0.75f, false);
						encoder.encode(bi);
					}
					break;
					case IMAGE_TYPE_GIF:
					{
						GifEncoder encoder = new GifEncoder(bi, out);
						encoder.encode();
					}
				}
			} catch (Exception io) {
				io.printStackTrace();
			}
		}
	}

	public static int getImageType(String contentType) {
		if (contentType.equals("image/gif")) {
			return IMAGE_TYPE_GIF;
		} else if (contentType.equals("image/jp2") || contentType.equals("image/jpg") || contentType.equals("image/jpeg") || contentType.equals("image/pjpeg") || contentType.equals("image/pjpg")) {
			return IMAGE_TYPE_JPG;
		} else if (contentType.equals("image/png")) {
			return IMAGE_TYPE_PNG;
		}
		return IMAGE_TYPE_UNKNOWN;
	}

	/*
	 * 
	 */
	public byte[] resize1(BufferedImage originalBufferedImage, int width) throws IOException {

		System.out.println("1 -" + new Date());
		originalBufferedImage.setAccelerationPriority(1);
		
		// get the dimension
        int originalWidth = originalBufferedImage.getWidth(null);
        int originalHeight = originalBufferedImage.getHeight(null);
        
        int resizedWidth = 0;
        int resizedHeight = 0;

        if (originalWidth > originalHeight) {
        	resizedWidth = width;
        	resizedHeight = (width * originalHeight) / originalWidth;
        } else {
        	resizedWidth = (width * originalWidth) / originalHeight;
        	resizedHeight = width;
        }

        System.out.println("original width: " + originalWidth);
        System.out.println("original height: " + originalHeight);
        System.out.println("resized width: " + resizedWidth);
        System.out.println("resized height: " + resizedHeight);

//        Image resizedImage = null;
//        Image resizedImage = originalImage.getScaledInstance(resizedWidth, resizedHeight, Image.SCALE_SMOOTH);

		System.out.println("2 -" + new Date());

		System.out.println("3 -" + new Date());
        BufferedImage resizedBufferedImage = new BufferedImage(resizedWidth, resizedHeight, originalBufferedImage.getType());

		System.out.println("4 -" + new Date());

        Graphics2D graphics2D = resizedBufferedImage.createGraphics();  
		System.out.println("5 -" + new Date());
		graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
		graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);		
		System.out.println("6 -" + new Date());
		graphics2D.drawImage(originalBufferedImage, 0, 0, resizedWidth, resizedHeight, 0, 0, originalWidth, originalHeight, null);  
		System.out.println("7 -" + new Date());
		graphics2D.dispose();  		

        /*
		AffineTransform at = new AffineTransform();
		at.scale(2.0, 2.0);
		AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
		resizedBufferedImage = scaleOp.filter(originalBufferedImage, resizedBufferedImage);
		*/

		// Copy image to buffered image.
//		Graphics2D graphics2D = bufferedImage.createGraphics();
        // Clear background and paint the image.
//		graphics2D.setColor(Color.white);
//		graphics2D.fillRect(0, 0, resizedWidth, resizedHeight);
//		graphics2D.drawImage(resizedImage, 0, 0, null);
//		graphics2D.dispose();

        // Soften.
		/*
		System.out.println("5 -" + new Date());
        float softenFactor = 0.05f;
        float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0};
        Kernel kernel = new Kernel(3, 3, softenArray);
        ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
        bufferedImage = cOp.filter(bufferedImage, null);
		System.out.println("6 - " + new Date());
		*/

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.flush();
        ImageIO.write(resizedBufferedImage, "jpeg", baos);
        baos.close();
		System.out.println("9 -" + new Date());
		return baos.toByteArray();
	}

	public byte[] resize2(BufferedImage originalImage, int width) throws IOException {

		int imageWidth = originalImage.getWidth(null);
        int imageHeight = originalImage.getHeight(null);
        
        int newWidth = 0;
        int newHeight = 0;

        if (imageWidth > imageHeight) {
        	newWidth = width;
        	newHeight = (width * imageHeight) / imageWidth;
        } else {
        	newWidth = (width * imageWidth) / imageHeight;
        	newHeight = width;
        }

        System.out.println("original width: " + imageWidth);
        System.out.println("original height: " + imageHeight);
        System.out.println("resized width: " + newWidth);
        System.out.println("resized height: " + imageHeight);
        
        BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
    	Graphics2D g = resizedImage.createGraphics();
    	g.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
    	g.dispose();	
    	g.setComposite(AlphaComposite.Src);
    	g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    	g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    	g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);        

        float softenFactor = 0.05f;
        float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0};
        Kernel kernel = new Kernel(3, 3, softenArray);
        ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
        resizedImage = cOp.filter(resizedImage, null);
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.flush();
        ImageIO.write(resizedImage, "jpeg", baos);
        baos.close();
        
        return baos.toByteArray();

	}
	
	public byte[] crop() {
		/*
		getSubimage
		public BufferedImage getSubimage(int x, int y, int w, int h);
		*/
		return null;
	}
	
}
